/***********************************************************************************************************************
 * ############################################# Application AdewebLite #############################################
 * AdewebLite Android est une application permettant de consulter des emplois du temps fournis par l'université UHP
 * avec ou sans connexion internet (informations gardées en mémoire à chaque synchronisation). Et utilisant la web
 * API ADE pour collecter les données.
 * @author Andrianjafintrimo Victor, Zanette Sébastien
 * 
 * Basée sur l'application du même nom, utilisant le flux RSS (données à traiter), de :
 * @author Schnell Michaël, Zanette Sébastien
 * **********************************************************************************************************************
 */

package ade.prime.database;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import ade.prime.model.Lesson;
import ade.prime.model.LessonHistory;
import ade.prime.model.LessonType;
import ade.prime.model.Project;
import ade.prime.model.Schedule;
import ade.prime.model.ScheduleState;
import ade.prime.model.StudentNumber;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils.InsertHelper;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

/**
 * Classe contenant des fonctions faisant les liens entre les requêtes SQL et
 * les Objets java.
 *
 * @author Zanette Sébastien,
 * @author Andrianjafintrimo Victorrimo
 * 
 * @version		1.1
 */
public class Database implements DatabaseConstants {

	/**
	 * Tag utilisé pour les messages de log.
	 */
	private static final String TAG = Database.class.getSimpleName() ;

	/**
	 * Utilisé pour accéder à la base de données.
	 */
	private SQLiteDatabase bdd ;
	
	/**
	 * Gère la base de données (notamment la création et la mise à jour).
	 */
	private CreateDatabases cd ;
	
	/**
	 * Optimise les insertions des cours d'un emploi du temps.
	 */
	private InsertHelper lessonInsert ;
	
	/**
	 * Optimise les insertions des anciens cours d'un emploi du temps (historique).
	 */
	private InsertHelper lessonHistoryInsert ;
	
	/**
	 * Utilisé pour formatter les dates au format utilisé dans la base de données.
	 */
	public static final SimpleDateFormat dateFormatter = 
			new SimpleDateFormat(DATE_FORMAT) ;
	
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
	 *                                                                                   *
	 * 		Fonctions globales                                                           *
	 *                                                                                   * 
	\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
	
	/**
	 * Unique constructeur. Crée la base de données si elle n'existe pas encore.
	 *
	 * @param	context
	 * 			contexte utilisé pour accéder à la base de données
	 */
	public Database(Context context) {
		super() ;
		this.cd = new CreateDatabases(context, NOM_BDD, null, VERSION_BDD) ;
	}// Database(Context)

	/**
	 * Ouvre la base de données en lecture et écriture.
	 * 
	 * @return	Retourne {@code true} si la base de données a été ouverte,
	 * 			{@code false} si elle était déjà ouverte
	 */
	public boolean open() {
		if ((this.bdd != null) && this.bdd.isOpen()) {
			return false ;
		}// if
		// Ouverture la BDD (en écriture)
		this.bdd = this.cd.getWritableDatabase() ;
		this.lessonInsert = new InsertHelper(this.bdd, TABLE_LESSON.getName()) ;
		this.lessonHistoryInsert = 
				new InsertHelper(this.bdd, TABLE_LESSON_HISTORY.getName()) ;
		return true ;
	}// open()

	/**
	 * Ferme la base de données en lecture et écriture.
	 */
	public void close() {
		// Fermeture de l'accès à la BDD 
		this.lessonInsert.close() ;
		this.lessonHistoryInsert.close() ;
		if (this.bdd.isOpen()) {
			this.bdd.close() ;
		}// if
	}// close()
	
	/**
	 * Retourne la valeur entière de la colonne dans le curseur fourni.
	 * 
	 * @param	cursor
	 * 			cursor source pour la valeur retournée
	 * 
	 * @param	columnName
	 * 			nom de la colonne source pour la valeur retournée
	 * 
	 * @return	La valeur entière de la colonne dans le curseur fourni
	 */
	protected static int getInt(Cursor cursor, String columnName) {
		return cursor.getInt(cursor.getColumnIndex(columnName)) ;
	}// getInt(Cursor, String)
	
	/**
	 * Retourne la chaîne de caractères contenue dans la colonne dans le 
	 * curseur fourni.
	 * 
	 * @param	cursor
	 * 			cursor source pour la valeur retournée
	 * 
	 * @param	columnName
	 * 			nom de la colonne source pour la valeur retournée
	 * 
	 * @return	La chaîne de caractères contenue dans la colonne dans le 
	 * 			curseur fourni
	 */
	protected static String getString(Cursor cursor, String columnName) {
		return cursor.getString(cursor.getColumnIndex(columnName)) ;
	}// getInt(Cursor, String)
	
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
	 *                                                                                   *
	 * 		Fonction relatives aux emplois du temps                                      *
	 *                                                                                   * 
	\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
	
	/**
	 * Insertion d'un emploi du temsp dans la base de données.
	 *
	 * @param	schedule
	 * 			informations de l'emploi du temps à insérer
	 * 
	 * @return 	retourne {@code -1} si échec, sinon la ligne du tuple inséré sinon
	 */
	public long insertSchedule(Schedule schedule) {
		boolean opened = this.open() ;
		
		// Création d'un ContentValues (fonctionne comme une HashMap)
		ContentValues values = new ContentValues() ;
		// L'id de l'emploi du temps est généré automatiquement
		// Le projet d'un empoi du temps est sauvegardé avec son ordinal
		values.put(TABLE_SCHEDULE_COL_NAME_PROJECT_ID, 
				Integer.valueOf(schedule.getProject().ordinal())) ;
		values.put(TABLE_SCHEDULE_COL_NAME_NAME, schedule.getName()) ;
		values.put(TABLE_SCHEDULE_COL_NAME_RESOURCES, schedule.getResources()) ;
		values.put(TABLE_SCHEDULE_COL_NAME_SUB_PERIOD, 
				Integer.valueOf(schedule.getPeriod())) ;
		values.put(TABLE_SCHEDULE_COL_NAME_LAST_UPDATE, schedule.getLastUpdate()) ;
		values.put(TABLE_SCHEDULE_COL_NAME_STUDENT_NUM, schedule.getStudentNum()) ;
		// L'état d'un empoi du temps est sauvegardé avec son ordinal
		values.put(TABLE_SCHEDULE_COL_NAME_STATUS, 
				Integer.valueOf(schedule.getState().ordinal())) ;
		values.put(TABLE_SCHEDULE_COL_NAME_CREATED, schedule.getCreated()) ;
		values.put(TABLE_SCHEDULE_COL_NAME_NOTIFY_UPDATE,
				schedule.isNotify() ? SCHEDULE_NOTIFY_TRUE : SCHEDULE_NOTIFY_FALSE) ;
		
		// Insertion dans la base
		long resInsert = -1 ;
		try {
			resInsert = this.bdd.insertOrThrow(TABLE_SCHEDULE_NAME, null, values) ;
		} catch (SQLException ex) {
			Log.e(TAG, String.format(
					"Echec de l'insertion de l'emploi du temps :\n%s", //$NON-NLS-1$ 
					schedule), ex) ;
		}// try

		if (resInsert != -1) {
			// On fixe l'idendifiant de l'emploi du temps crée
			schedule.setId(this.getScheduleId(resInsert)) ;
			
			// Ajout des cours de l'emploi du temps
			for (Lesson lesson : schedule) {
				this.insertLesson(lesson) ;
			}// for
			
			for (StudentNumber sn : schedule.getStudentNumList()) {
				StudentNumber snFromDb =
						this.getRessourceWithStudentNumber(sn.getStudentNumber()) ;
				// Le numéro étudiant n'existe pas encore dans la base de données
				if (snFromDb == null) {
					this.insertStudentNumber(sn) ;
				} else {
					this.updateStudentNumber(sn.getStudentNumber(), sn) ;
				}// if
			}// for
			
		}// if
		if (opened) {
			this.close() ;
		}// if
		
		return resInsert ;
	}// insertSchedule(Schedule)
	
	private int getScheduleId(long rowId) {
		String where = String.format(WHERE_ROWID, Long.valueOf(rowId)) ;
		Cursor cursor = this.bdd.query(TABLE_SCHEDULE_NAME, 
				new String[]{TABLE_SCHEDULE_COL_NAME_ID}, 
				where, //where
				null, //whereArgs
				null, //groupBy
				null, //having
				null) ;//orderBy
		cursor.moveToNext() ;
		return getInt(cursor, TABLE_SCHEDULE_COL_NAME_ID) ;
	}// getScheduleId(long)

	/**
	 * Met à jour un emploi du temps identifié par son id.
	 *
	 * @param	scheduleId
	 * 			identifiant de l'emploi du temps modifié
	 * 
	 * @param	newSchedule
	 * 			nouvelles valeurs pour l'emloi du temps
	 * 
	 * @return  Retourne {@code -1} si échec, sinon le nombre de lignes mises à jour
	 */
	public int updateSchedule(int scheduleId, Schedule newSchedule) {
		boolean opened = this.open() ;
		
		// On récupère les anciennes valeurs de l'emploi du temps
		Schedule oldSchedule = this.getSchedule(scheduleId) ;
		int resUpdate = this.updateSchedule(oldSchedule, newSchedule) ;
		
		if (opened) {
			this.close() ;
		}// if
		return resUpdate ;
	}// updateSchedule(int, Schedule)
	
	/**
	 * Met à jour un emploi du temps identifié par son id.
	 *
	 * @param	scheduleId
	 * 			identifiant de l'emploi du temps modifié
	 * 
	 * @param	newSchedule
	 * 			nouvelles valeurs pour l'emloi du temps
	 * 
	 * @return  Retourne {@code -1} si échec, sinon le nombre de lignes mises à jour
	 */
	public int updateSchedule(Schedule oldSchedule, Schedule newSchedule) {		
		boolean opened = this.open() ;
		
		// Récupération des nouvelles valeurs
		boolean modified = false ;
		ContentValues values = new ContentValues() ;
		String newName = newSchedule.getName() ;
		if (!oldSchedule.getName().equals(newName)) {
			values.put(TABLE_SCHEDULE_COL_NAME_NAME, newName) ;
			modified = true ;
		}// if
		Project newProject = newSchedule.getProject() ;
		if (!oldSchedule.getProject().equals(newProject)) {
			values.put(TABLE_SCHEDULE_COL_NAME_PROJECT_ID, 
					Integer.valueOf(newProject.ordinal())) ;
			modified = true ;
		}// if
		String newResources = newSchedule.getResources() ;
		if (!oldSchedule.getResources().equals(newResources)) {
			values.put(TABLE_SCHEDULE_COL_NAME_RESOURCES, newResources) ;
			modified = true ;
		}// if
		String newStudentNum = newSchedule.getStudentNum() ;
		if (!oldSchedule.getStudentNum().equals(newStudentNum)) {
			values.put(TABLE_SCHEDULE_COL_NAME_STUDENT_NUM, newStudentNum) ;
			modified = true ;
		}// if
		int newPeriod = newSchedule.getPeriod() ;
		if (oldSchedule.getPeriod() != newPeriod) {
			values.put(TABLE_SCHEDULE_COL_NAME_SUB_PERIOD, 
					Integer.valueOf(newPeriod)) ;
			modified = true ;
		}// if
		String newLastUpdate = newSchedule.getLastUpdate() ;
		if (!oldSchedule.getLastUpdate().equals(newLastUpdate)) {
			values.put(TABLE_SCHEDULE_COL_NAME_LAST_UPDATE, newLastUpdate) ;
			modified = true ;
		}// if
		ScheduleState newState = newSchedule.getState() ;
		if (!oldSchedule.getState().equals(newState)) {
			values.put(TABLE_SCHEDULE_COL_NAME_STATUS, 
					Integer.valueOf(newState.ordinal())) ;
			modified = true ;
		}// if
		String newCreated = newSchedule.getCreated() ;
		if (!oldSchedule.getCreated().equals(newCreated)) {
			values.put(TABLE_SCHEDULE_COL_NAME_CREATED, newCreated) ;
			modified = true ;
		}// if
		boolean newNotify = newSchedule.isNotify() ;
		if (oldSchedule.isNotify() != newNotify) {
			values.put(TABLE_SCHEDULE_COL_NAME_NOTIFY_UPDATE, 
					Boolean.valueOf(newNotify)) ;
			modified = true ;
		}// if
		
		int resUpdate = -1 ;
		if (modified) {
			String whereClause = String.format(
					TABLE_SCHEDULE_WHERE_ID, Integer.valueOf(oldSchedule.getId())) ;
			this.bdd.beginTransaction() ;
			resUpdate = this.bdd.update(TABLE_SCHEDULE.getName(), 
					values, 
					whereClause, 
					null) ;
			boolean success = (resUpdate > 0) ;
			if (success) {
				for (StudentNumber sn : newSchedule.getStudentNumList()) {
					int tmpResUpdate = 
							this.updateStudentNumber(sn.getStudentNumber(), sn) ;
					if (tmpResUpdate <= 0) {
						success = false ;
						break ;
					}// if
					resUpdate += tmpResUpdate ;
				}// for
			}// for
			if (success) {
				this.bdd.setTransactionSuccessful() ;
			}// if
			this.bdd.endTransaction() ;
		}// if
		if (opened) {
			this.close() ;
		}// if
		
		return resUpdate ;
	}// updateSchedule(int, Schedule)

	/**
	 * Retourne l'emploi du temps avec l'identifiant fourni.
	 *
	 * @param	scheduleId
	 * 			identifiant de l'emploi du temps demandé
	 * 
	 * @return	Instance de {@code Schedule} avec les valeurs de l'emploi du temps
	 * 			demandé ou {@code null} s'il n'existe pas d'emploi du temps avec
	 * 			l'identifiant fourni
	 */
	public Schedule getSchedule(int scheduleId) {
		boolean opened = this.open() ;
		
		Schedule schedule = null ;
		Cursor cursor = null ;
		String whereClause = String.format(TABLE_SCHEDULE_WHERE_ID, 
				Integer.valueOf(scheduleId)) ;
		cursor = this.bdd.query(TABLE_SCHEDULE.getName(), 
				TABLE_SCHEDULE.getColumnNames(), 
				whereClause, null, null, null, null) ; 
		schedule = cursorToSchedule(cursor) ;
		cursor.close() ;
		
		if (opened) {
			this.close() ;
		}// if
		return schedule ;
	}// getSchedule(int)

	/**
	 * Met à jour l'état d'un emploi du temps identifié par son identifiant.
	 *
	 * @param	scheduleId
	 * 			identifiant de l'emploi du temps modifié
	 * 
	 * @return  Retourne {@code -1} si échec, sinon le nombre de lignes mises à jour
	 */
	public int updateScheduleState(int scheduleId, ScheduleState newState) {
		boolean opened = this.open() ;
		
		// Mise à jour de l'état
		ContentValues values = new ContentValues() ;
		values.put(TABLE_SCHEDULE_COL_NAME_STATUS, 
				Integer.valueOf(newState.ordinal())) ;
		String whereClause = String.format(TABLE_SCHEDULE_WHERE_ID, 
				Integer.valueOf(scheduleId)) ;
		int resUpdate = this.bdd.update(TABLE_SCHEDULE.getName(), 
				values, whereClause, null) ;
		
		if (opened) {
			this.close() ;
		}// if
		return resUpdate ;
	}// updateScheduleState(int, ScheduleState)

	/**
	 * Supprime un emploi du temps ainsi que tous les cours associés.
	 *
	 * @param	scheduleId
	 * 			identifiant de l'emploi du temps à supprimer
	 * 
	 * @return 	Le nombre de tuples supprimés
	 * 
	 * @see	#removeScheduleLessons(int)
	 */
	public int removeSchedule(int scheduleId) {
		boolean opened = this.open() ;
		this.removeScheduleLessons(scheduleId) ;
		String whereClause = String.format(TABLE_SCHEDULE_WHERE_ID, 
				Integer.valueOf(scheduleId)) ;
		int errSuppr = this.bdd.delete(TABLE_SCHEDULE.getName(), whereClause, null) ;
		
		if (opened) {
			this.close() ;
		}// if
		return errSuppr;
	}// removeSchedule(int)

	/**
	 * Supprime tous les cours associées à un emploi du temps.
	 *
	 * @param	scheduleId
	 * 			identifiant de l'emploi du temps dont les cours sont à supprimer
	 * 
	 * @return 	Le nombre de tuples supprimés
	 */
	public int removeScheduleLessons(int scheduleId) {
		int res = 0 ; 
		String whereClause ;
		
		// Delete lesson history 
		whereClause = String.format(TABLE_LESSON_HISTORY_WHERE_SCHEDULE, 
				Integer.valueOf(scheduleId)) ;
		res += this.bdd.delete(TABLE_LESSON_HISTORY.getName(), whereClause, null) ;

		// Delete lessons 
		whereClause = String.format(TABLE_LESSON_WHERE_SCHEDULE, 
				Integer.valueOf(scheduleId)) ;
		res += this.bdd.delete(TABLE_LESSON.getName(), whereClause, null) ;
		
		return res ;
	}// removeScheduleLessons(int)

	/**
	 * Retourne une instance de {@code Schedule} initialisée avec les valeurs
	 * du prochain tuple du curseur fourni.
	 *
	 * @param	cursor
	 * 			source des valeurs pour initialiser l'emploi du temps
	 * 
	 * @return	instance de {@code Schedule} ou {@code null} si aucun
	 * 			emploi du temps n'a pu être créé 
	 */
	protected static Schedule cursorToSchedule(Cursor cursor) {
		Schedule result = null ;
		if ((cursor != null) && cursor.moveToNext()) {
			// Création d'un emploi du temps vide
			result = new Schedule() ;
			
			// Initialisation de l'emploi du temps
			result.setId(getInt(cursor, TABLE_SCHEDULE_COL_NAME_ID)) ;
			result.setName(getString(cursor, TABLE_SCHEDULE_COL_NAME_NAME)) ;
			result.setProject(getProject(cursor, TABLE_SCHEDULE_COL_NAME_PROJECT_ID)) ;
			result.setResources(getString(cursor, TABLE_SCHEDULE_COL_NAME_RESOURCES)) ;
			result.setPeriod(getInt(cursor, TABLE_SCHEDULE_COL_NAME_SUB_PERIOD)) ;
			result.setLastUpdate(getString(cursor, TABLE_SCHEDULE_COL_NAME_LAST_UPDATE)) ;
			result.setState(getScheduleState(cursor, TABLE_SCHEDULE_COL_NAME_STATUS)) ;
			result.setCreated(getString(cursor, TABLE_SCHEDULE_COL_NAME_CREATED)) ;
			result.setStudentNum(getString(cursor, TABLE_SCHEDULE_COL_NAME_STUDENT_NUM)) ;
			result.setNotify(
					getInt(cursor, TABLE_SCHEDULE_COL_NAME_NOTIFY_UPDATE) == SCHEDULE_NOTIFY_TRUE.intValue()) ;
		}// if
		
		return result;
	}// cursorToSchedule(Cursor)
	
	/**
	 * Retourne l'instance de {@code Project} contenu dans la colonne dans le 
	 * curseur fourni.
	 * 
	 * @param	cursor
	 * 			cursor source pour la valeur retournée
	 * 
	 * @param	columnName
	 * 			nom de la colonne source pour la valeur retournée
	 * 
	 * @return	Instance de {@code Project} contenu dans la colonne dans le 
	 * 			curseur fourni
	 */
	protected static Project getProject(Cursor cursor, String columnName) {
		int projectOrdinal = getInt(cursor, columnName) ;
		return Project.values()[projectOrdinal] ;
	}// getProject(Cursor, String)
	
	/**
	 * Retourne l'instance de {@code ScheduleState} contenu dans la colonne dans le 
	 * curseur fourni.
	 * 
	 * @param	cursor
	 * 			cursor source pour la valeur retournée
	 * 
	 * @param	columnName
	 * 			nom de la colonne source pour la valeur retournée
	 * 
	 * @return	Instance de {@code ScheduleState} contenu dans la colonne dans le 
	 * 			curseur fourni
	 */
	protected static ScheduleState getScheduleState(Cursor cursor, String columnName) {
		int stateOrdinal = getInt(cursor, columnName) ;
		return ScheduleState.values()[stateOrdinal] ;
	}// getScheduleState(Cursor, String)

	/**
	 * Retourne un tableau de {@code String} contenant les noms de tous les emplois
	 * du temps.
	 *
	 * @return 	Instance de {@code String[]} contenant le nom de tous les emplois
	 * 			du temps
	 */
	public String[] getSchedulesName() {
		boolean opened = this.open() ;
		
		Cursor cursor = this.bdd.query(TABLE_SCHEDULE.getName(), 
				new String[]{TABLE_SCHEDULE_COL_NAME_NAME},
				null, null, null, null, null) ;
		String[] names = new String[cursor.getCount()] ;
		int index = 0 ;
		while (cursor.moveToNext()) {
			names[index] = getString(cursor, TABLE_SCHEDULE_COL_NAME_NAME) ;
			index++ ;
		}// while
		cursor.close() ;

		if (opened) {
			this.close() ;
		}// if
		return names ;
	}// getScheduleNames()

	/**
	 * Retourne un tableau de {@code int} contenant les identifiants de tous les 
	 * emplois du temps.
	 *
	 * @return 	Instance de {@code int[]} contenant l'identifiant de tous les emplois
	 * 			du temps
	 */
	public int[] getAllSchedulesIds() {
		boolean opened = this.open() ;
		
		Cursor cursor = this.bdd.query(TABLE_SCHEDULE.getName(), 
				new String[]{TABLE_SCHEDULE_COL_NAME_ID},
				null, null, null, null, null) ;
		int[] ids = new int[cursor.getCount()] ;
		int index = 0 ;
		while (cursor.moveToNext()) {
			ids[index] = getInt(cursor, TABLE_SCHEDULE_COL_NAME_ID) ;
			index++ ;
		}// while
		cursor.close() ;

		if (opened) {
			this.close() ;
		}// if
		return ids ;
	}// getAllSchedulesIds()

	/**
	 * Retourne la liste de tous les emplois du temps.
	 *
	 * @return 	Instance de {@code List<Schedule>} contenant tous les emplois
	 * 			du temps
	 */
	public List<Schedule> getAllSchedules() {
		boolean opened = this.open() ;
		
		Cursor cursor = this.bdd.query(TABLE_SCHEDULE.getName(), 
				TABLE_SCHEDULE.getColumnNames(),
				null, null, null, null, null) ;
		List<Schedule> schedules = new ArrayList<Schedule>() ;
		Schedule next = cursorToSchedule(cursor) ;
		while (next != null) {
			schedules.add(next) ;
			next = cursorToSchedule(cursor) ;
		}// while
		cursor.close() ;

		if (opened) {
			this.close() ;
		}// if
		return schedules ;
	}// getAllSchedules()

	/**
	 * Fixe la date de dernière mise à jour d'un emploi du temps à maintenant.
	 *
	 * @param 	scheduleId
	 * 			identifiant de l'emploi du temps à mette à jour
	 * 
	 * @return 	Le nombre de tuples mis à jour
	 */
	public int updateLastUpdateForSchedule(int scheduleId) {
		boolean opened = this.open() ;
		
		// Mise à jour
		ContentValues values = new ContentValues() ;
		values.put(TABLE_SCHEDULE_COL_NAME_LAST_UPDATE, 
				dateFormatter.format(new Date())) ;
		String whereClause = String.format(TABLE_SCHEDULE_WHERE_ID, 
				Integer.valueOf(scheduleId)) ;
		int resUpt = this.bdd.update(
				TABLE_SCHEDULE.getName(), values, whereClause, null) ;
		
		if (opened) {
			this.close() ;
		}// if
		return resUpt ;
	}// updateLastUpdateForSchedule(String)

	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
	 *                                                                                   *
	 * 		Fonction relatives aux cours                                                 *
	 *                                                                                   * 
	\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
	
	/**
	 * Insère un cours dans la base de données.
	 *
	 * @param	lesson
	 * 			cours à insérer dans la base
	 * 
	 * @return 	{@code -1} si échec, la ligne du tuple inséré sinon
	 */
	public long insertLesson(Lesson lesson) {
		// Prepare insert data
		ContentValues values = new ContentValues() ;
		values.put(TABLE_LESSON_COL_NAME_TITLE, lesson.getTitle()) ;
		values.put(TABLE_LESSON_COL_NAME_TIME, lesson.getTime()) ;
		values.put(TABLE_LESSON_COL_NAME_SCHEDULE, 
				Integer.valueOf(lesson.getScheduleId())) ;
		values.put(TABLE_LESSON_COL_NAME_TYPE, 
				Integer.valueOf(lesson.getType().ordinal())) ;
		values.put(TABLE_LESSON_COL_NAME_CHANGE, Integer.valueOf(lesson.getChange())) ;
		values.put(TABLE_LESSON_COL_NAME_CREATED, lesson.getCreated()) ;
		values.put(TABLE_LESSON_COL_NAME_ROOMS, lesson.getRoom()) ;
		values.put(TABLE_LESSON_COL_NAME_GROUPS, lesson.getGroups()) ;
		values.put(TABLE_LESSON_COL_NAME_TEACHERS, lesson.getTeacher()) ;
		values.put(TABLE_LESSON_COL_NAME_COLOR, lesson.getColor()) ;
		values.put(TABLE_LESSON_COL_NAME_NOTE, lesson.getNote()) ;
		values.put(TABLE_LESSON_COL_NAME_GUID, lesson.getGuid()) ;

		return this.lessonInsert.insert(values) ;
	}// insertLesson(Lesson)

	/**
	 * Met à jour un cours dans la base de données.
	 *
	 * @param	lessonId
	 * 			identifiant du cours à modifier
	 * 
	 * @param 	newLesson
	 * 			nouvelles valeurs du cours
	 * 
	 * @return  Le nombre de tuples mis à jour
	 */
	public int updateLesson(long lessonId, Lesson newLesson) {
		boolean opened = this.open() ;

		// Retrieve old values
		Lesson oldLesson = this.getLesson(lessonId) ;
		
		// Retrieve the changed values
		ContentValues values = new ContentValues() ;
		String newTitle = newLesson.getTitle() ;
		if (!oldLesson.getTitle().equals(newTitle)) {
			values.put(TABLE_LESSON_COL_NAME_TITLE, newTitle) ;
		}// if
		String newTime = newLesson.getTime() ;
		if (!oldLesson.getTime().equals(newTime)) {
			values.put(TABLE_LESSON_COL_NAME_TIME, newTime) ;
		}// if
		int newSchedule = newLesson.getScheduleId() ;
		if (oldLesson.getScheduleId() != newSchedule) {
			values.put(TABLE_LESSON_COL_NAME_SCHEDULE, Integer.valueOf(newSchedule)) ;
		}// if
		LessonType newType = newLesson.getType() ;
		if (!oldLesson.getType().equals(newType)) {
			values.put(TABLE_LESSON_COL_NAME_TYPE, Integer.valueOf(newType.ordinal())) ;
		}// if
		int newChange = newLesson.getChange() ;
		if (oldLesson.getChange() != newChange) {
			values.put(TABLE_LESSON_COL_NAME_CHANGE, Integer.valueOf(newChange)) ;
		}// if
		String newRoom = newLesson.getRoom() ;
		if (!oldLesson.getRoom().equals(newRoom)) {
			values.put(TABLE_LESSON_COL_NAME_ROOMS, newRoom) ;
		}// if
		String newTeacher = newLesson.getTeacher() ;
		if (!oldLesson.getTeacher().equals(newTeacher)) {
			values.put(TABLE_LESSON_COL_NAME_TEACHERS, newTeacher) ;
		}// if
		String newCreated = newLesson.getCreated() ;
		if (!oldLesson.getCreated().equals(newCreated)) {
			values.put(TABLE_LESSON_COL_NAME_CREATED, newCreated) ;
		}// if
		String newColor = newLesson.getColor() ;
		if (!oldLesson.getColor().equals(newColor)) {
			values.put(TABLE_LESSON_COL_NAME_COLOR, newColor) ;
		}// if
		String newNote = newLesson.getNote() ;
		if (!oldLesson.getNote().equals(newNote)) {
			values.put(TABLE_LESSON_COL_NAME_ROOMS, newNote) ;
		}// if
		String newGroup = newLesson.getGroups() ;
		if (!oldLesson.getGroups().equals(newGroup)) {
			values.put(TABLE_LESSON_COL_NAME_GROUPS, newGroup) ;
		}// if
		
		// Update data base
		String whereClause = String.format(TABLE_LESSON_WHERE_ID, 
				Long.valueOf(lessonId)) ; 
		int resUpt = this.bdd.update(TABLE_LESSON.getName(), 
				values, whereClause, null) ;
		// Update history
		LessonHistory newHistory = newLesson.getHistory() ;
		LessonHistory oldHistory = oldLesson.getHistory() ;
//		Log.d(TAG, String.format(">>> OldH\n%s\n > NewH\n%s\n>>>", //$NON-NLS-1$
//				oldHistory, newHistory)) ;
		if (newHistory != null) {
			if (oldHistory == null) {
				this.insertLessonHistory(newHistory) ;
			} else if (!oldHistory.equals(newHistory)) {
				this.updateLessonHistory(lessonId, newHistory) ;
			}// if
		}// if
		
		if (opened) {
			this.close() ;
		}// if
		return resUpt ;
	}// updateLesson(long, Lesson)

	/**
	 * Met à jour le type d'un cours.
	 *
	 * @param	lessonId
	 * 			identifiant du cours à modifier
	 * 
	 * @param	newType
	 * 			nouveau type du cours à modifier
	 * 
	 * @return 	Le nombre de lignes mises à jour
	 */
	public int updateLessonType(long lessonId, LessonType newType) {
		boolean opened = this.open() ;
		
		ContentValues values = new ContentValues() ;
		values.put(TABLE_LESSON_COL_NAME_TYPE, Integer.valueOf(newType.ordinal())) ;
		String whereClause = String.format(TABLE_LESSON_WHERE_ID, 
				Long.valueOf(lessonId)) ;
		int resUpt = this.bdd.update(TABLE_LESSON.getName(), 
				values, whereClause, null) ;
		
		if (opened) {
			this.close() ;
		}// if
		return resUpt ;
	}//updateStatutLesson(long id,String scheduleName,int newStatut)   

	/**
	 * Supprime un cours de la base de données.
	 *
	 * @param	lessonId
	 * 			identifiant du cours à supprimer
	 * 
	 * @return	Le nombre de tuples supprimés
	 * 
	 * @deprecated La supression se fait via le nom de l'emploi du temps 
	 * 			(voir {@link #removeScheduleLessonsWithName(String)})
	 */
	@Deprecated
	public int removeLesson(long lessonId) {
		boolean opened = this.open() ;

		String whereClause = String.format(TABLE_LESSON_WHERE_ID, 
				Long.valueOf(lessonId)) ;
		int resDel = 
				this.bdd.delete(TABLE_LESSON.getName(), whereClause, null) ;
		
		if (opened) {
			this.close() ;
		}// if
		return resDel ;
	}// removeLesson(long)

	/**
	 * Retourne le cours avec l'identifiant fourni.
	 *
	 * @param	lessonId
	 * 			identifiant du cours demandé
	 * 
	 * @return	instance de {@code Lesson} représentant le cours ou {@code null}
	 * 			si aucun cours n'existe avec l'identifiant fourni
	 */
	public Lesson getLesson(long lessonId) {
		boolean opened = this.open() ;
		
		String whereClause = String.format(TABLE_LESSON_WHERE_ID, 
				Long.valueOf(lessonId)) ;
		Cursor cursor = this.bdd.query(
				VIEW_LESSON_WITH_HISTORY.getName(),
				VIEW_LESSON_WITH_HISTORY.getColumnNames(),
				whereClause,
				null, null, null, null, null) ;
		Lesson lesson = cursorToLesson(cursor) ;
		lesson.setHistory(cursorToLessonHistory(cursor, false)) ;
		cursor.close() ;
		
		if (opened) {
			this.close() ;
		}// if
		
		return lesson;
	}// getLesson(long lessonId)

	/**
	 * Retourne une instance de {@code Lesson} initialisée avec les valeurs
	 * du prochain tuple du curseur fourni.
	 *
	 * @param	cursor
	 * 			source des valeurs pour initialiser le cours
	 * 
	 * @return	instance de {@code Lesson} ou {@code null} si aucun
	 * 			cours n'a pu être créé 
	 */
	public static Lesson cursorToLesson(Cursor cursor) {
		Lesson lesson = null ;
		if ((cursor != null) && cursor.moveToNext()) {
			lesson = new Lesson() ;
			lesson.setLessonId(getInt(cursor, TABLE_LESSON_COL_NAME_ID)) ;
			lesson.setTitle(getString(cursor, TABLE_LESSON_COL_NAME_TITLE)) ;
			lesson.setTime(getString(cursor, TABLE_LESSON_COL_NAME_TIME)) ;
			lesson.setScheduleId(getInt(cursor, TABLE_LESSON_COL_NAME_SCHEDULE)) ;
			lesson.setType(getLessonType(cursor, TABLE_LESSON_COL_NAME_TYPE)) ;
			lesson.setChange(getInt(cursor, TABLE_LESSON_COL_NAME_CHANGE)) ;
			lesson.setCreated(getString(cursor, TABLE_LESSON_COL_NAME_CREATED)) ;
			lesson.setRoom(getString(cursor, TABLE_LESSON_COL_NAME_ROOMS)) ;
			lesson.setGroups(getString(cursor, TABLE_LESSON_COL_NAME_GROUPS)) ;
			lesson.setTeachers(getString(cursor, TABLE_LESSON_COL_NAME_TEACHERS)) ;
			lesson.setColor(getString(cursor, TABLE_LESSON_COL_NAME_COLOR)) ;
			lesson.setNote(getString(cursor, TABLE_LESSON_COL_NAME_NOTE)) ;
			lesson.setGuid(getString(cursor, TABLE_LESSON_COL_NAME_GUID)) ;
			lesson.setHistory(cursorToLessonHistory(cursor, false)) ;
		}// if
		
		return lesson ;
	}// cursorToLesson(Cursor)

	/**
	 * Retourne une instance de {@code LessonHistory} initialisée avec les valeurs
	 * du prochain tuple du curseur fourni.
	 *
	 * @param	cursor
	 * 			source des valeurs pour initialiser le cours
	 * 
	 * @return	instance de {@code LessonHistory} ou {@code null} si aucun
	 * 			cours n'a pu être créé 
	 */
	public static LessonHistory cursorToLessonHistory(Cursor cursor) {
		return cursorToLessonHistory(cursor, true) ;
	}// cursorToLessonHistory(Cursor)

	/**
	 * Retourne une instance de {@code LessonHistory} initialisée avec les valeurs
	 * du prochain tuple du curseur fourni.
	 *
	 * @param	cursor
	 * 			source des valeurs pour initialiser le cours
	 * 
	 * @param	moveToNext
	 * 			indique s'il faut déplacer le curseur vers le prochain tuple
	 * 			avant de récupérer les valeurs
	 * 
	 * @return	instance de {@code LessonHistory} ou {@code null} si aucun
	 * 			cours n'a pu être créé 
	 */
	public static LessonHistory cursorToLessonHistory(Cursor cursor,
			boolean moveToNext) {
		LessonHistory history = null ;
		if ((cursor != null) && (!moveToNext || cursor.moveToNext())) {
			String time = getString(cursor, TABLE_LESSON_HISTORY_COL_NAME_TIME) ;
			String rooms = getString(cursor, TABLE_LESSON_HISTORY_COL_NAME_ROOMS) ;
			if ((time != null) || (rooms != null)) {
				history = new LessonHistory() ;
				history.setId(getInt(cursor, TABLE_LESSON_HISTORY_COL_NAME_LESSON_ID)) ;
				history.setTime(time) ;
				history.setRooms(rooms) ;
			}// if
		}// if
		
		return history ;
	}// cursorToLessonHistory(Cursor, boolean)
	
	/**
	 * Retourne l'instance de {@code LessonType} contenue dans la colonne dans le 
	 * curseur fourni.
	 * 
	 * @param	cursor
	 * 			cursor source pour la valeur retournée
	 * 
	 * @param	columnName
	 * 			nom de la colonne source pour la valeur retournée
	 * 
	 * @return	Instance de {@code LessonType} contenu dans la colonne dans le 
	 * 			curseur fourni
	 */
	protected static LessonType getLessonType(Cursor cursor, String columnName) {
		int lessonTypeOrdinal = getInt(cursor, columnName) ;
		return LessonType.values()[lessonTypeOrdinal] ;
	}// getProject(Cursor, String)

	/**
	 * Retourne tous les cours d'un emploi du temps.
	 *
	 * @param	scheduleId
	 * 			identifiant de l'emploi du temps
	 * 
	 * @return	Instance de {@code Lesson[]} avec tous les cours de l'emploi du temps
	 * 			indiqué
	 */
	public Lesson[] getAllScheduleLessons(int scheduleId) {
		boolean opened = this.open() ;
		
		String whereClause = String.format(TABLE_LESSON_WHERE_SCHEDULE, 
				Integer.valueOf(scheduleId)) ;
		String orderByClause = String.format("%s ASC", //$NON-NLS-1$
				TABLE_LESSON_COL_NAME_TIME) ; 
		Cursor cursor = this.bdd.query(VIEW_LESSON_WITH_HISTORY.getName(),
				VIEW_LESSON_WITH_HISTORY.getColumnNames(), whereClause,
				null, null, null, orderByClause, null) ;

		Lesson[] lessons = new Lesson[cursor.getCount()] ;
		int index = 0, lastIndex = lessons.length ;
		while (index < lastIndex) {
			Lesson lesson = cursorToLesson(cursor) ;
			lesson.setHistory(cursorToLessonHistory(cursor, false)) ;
			lessons[index] = lesson ;
			index++ ;
		} // while
		
		cursor.close() ;
		if (opened) {
			this.close() ;
		}// if
		return lessons ;
	}// getAllScheduleLessons(int)

	/**
	 * Retourne tous les cours d'un emploi du temps dont la date dépasse la date
	 * courante. (cours qui ne sont pas encore écoulés)
	 *
	 * @param	scheduleId
	 * 			identifiant de l'emploi du temps
	 * 
	 * @return	Instance de {@code Lesson[]} avec tous les cours de l'emploi du temps
	 * 			indiqué
	 */
	public Lesson[] getFutureScheduleLessons(int scheduleId) {
		boolean opened = this.open() ;
		
		String whereClause = String.format(TABLE_LESSON_WHERE_SCHEDULE_AND_FUTURE, 
				Integer.valueOf(scheduleId)) ;
		String orderByClause = String.format("%s ASC", //$NON-NLS-1$
				TABLE_LESSON_COL_NAME_TIME) ; 
		Cursor cursor = this.bdd.query(VIEW_LESSON_WITH_HISTORY.getName(),
				VIEW_LESSON_WITH_HISTORY.getColumnNames(), whereClause,
				null, null, null, orderByClause, null) ;

		Lesson[] lessons = new Lesson[cursor.getCount()] ;
		int index = 0, lastIndex = lessons.length ;
		while (index < lastIndex) {
			Lesson lesson = cursorToLesson(cursor) ;
			lesson.setHistory(cursorToLessonHistory(cursor, false)) ;
			lessons[index] = lesson ;
			index++ ;
		} // while
		
		cursor.close() ;
		if (opened) {
			this.close() ;
		}// if
		return lessons ;
	}// getFutureScheduleLessons(int)

	/**
	 * Insère tous les cours fournis dans la base de données.
	 *
	 * @param	lessons
	 * 			liste des cours à insérer
	 * 
	 * @return	Le nombre de ligne ajoutés ou {@code -1} si l'insertion d'un cours
	 * 			à échouée
	 */
	public long insertLessons(List<Lesson> lessons) {
//		Log.d(TAG, "[insertLessons] >>") ;
		boolean opened = this.open() ;
		int resultInsert = -1 ;
//		try {
		this.bdd.beginTransaction() ;
		boolean success = true ;
		for (Lesson lesson : lessons) {
			long result = this.insertLesson(lesson) ;
//			Log.d(TAG, "  -- insert result: " + result) ;
			if (result == -1L) {
				success = false ;
				break ;
			}// if
		}// for
		if (success) {
			this.bdd.setTransactionSuccessful() ;
			resultInsert = lessons.size() ;
		}// if
		this.bdd.endTransaction() ;
		
		if (opened) {
			this.close() ;
		}// if
//		} catch(Exception e) {
//			Log.d(TAG, "[insertLessons]: failed !", e) ;
//		}
//		Log.d(TAG, "<< [insertLessons]") ;
		return resultInsert ;
	}// insertLessons(List<Lesson>)

	/**
	 * Met à jour tous les cours de la liste fournie.
	 *
	 * @param	lessons
	 * 			liste des cours avec leurs nouvelles valeurs
	 * 
	 * @return	Le nombre de tuples mis à jour
	 */
	public int updateLessons(List<Lesson> lessons) {
		int resUpdate = 0 ;
		for (Lesson lesson : lessons) {
			resUpdate += this.updateLesson(lesson.getLessonId(), lesson) ;
		}// for
		return resUpdate ;
	}// updateLessons(List<Lesson>)

	/**
	 * Supprime tous les cours de la liste fournie.
	 *
	 * @param	lessons
	 * 			liste des cours à supprimer
	 * 
	 * @return	Le nombre de tuples supprimés
	 */
	public int removeLessons(List<Lesson> lessons) {
		boolean opened = this.open();
		int resDelete = 0 ;
		
		StringBuilder idsList = new StringBuilder() ;
		String in_separator = "," ; //$NON-NLS-1$
		for(Lesson lesson : lessons) {
			idsList.append(in_separator) ;
			idsList.append(lesson.getLessonId()) ;
		}// for
		String whereClause = String.format(TABLE_LESSON_WHERE_ID_IN,
				idsList.substring(in_separator.length())) ;
		resDelete = this.bdd.delete(TABLE_LESSON.getName(), whereClause, null) ;
		
		if (opened) {
			this.close() ;
		}// if
		return resDelete ;
	}// updateLessons(List<Lesson>)

	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
	 *                                                                                   *
	 * 		Fonction relatives aux numéros étudiants                                     *
	 *                                                                                   * 
	\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
	
	/**
	 * Insère un numéro étudiant et ses ressources associées dans la base de données.
	 *
	 * @param	sn
	 * 			{@code StudentNumber} contenant les informations à stocker
	 * 
	 * @return  {@code -1} si échec, sinon la ligne du tuple inséré
	 */
	public long insertStudentNumber(StudentNumber sn) {
		boolean opened = this.open() ;
		
		ContentValues values = new ContentValues() ;
		values.put(TABLE_STUDENT_NUMBER_COL_NAME_STUDENT_NUMBER, 
				sn.getStudentNumber()) ;
		values.put(TABLE_STUDENT_NUMBER_COL_NAME_RESOURCES, sn.getResources()) ;
		
		long resInsert =
				this.bdd.insertOrThrow(TABLE_STUDENT_NUMBER.getName(), null, values) ;
		
		if (opened) {
			this.close() ;
		}// if
		return resInsert ;
	}// insertStudentNumber(StudentNumber)

	/**
	 * Met à jour un numéro étudiant et ses ressources associées.
	 *
	 * @param	number
	 * 			numéro étudiant à mettre à jour
	 * 
	 * @param 	sn
	 * 			nouvelles valeurs pour le numéro étudiant fourni
	 * 
	 * @return  Le nombre de typles mis à jour
	 */
	public int updateStudentNumber(String number, StudentNumber sn) {
		boolean opened = this.open() ;
		
		ContentValues values = new ContentValues() ;
		values.put(TABLE_STUDENT_NUMBER_COL_NAME_STUDENT_NUMBER, sn.getStudentNumber()) ;
		values.put(TABLE_STUDENT_NUMBER_COL_NAME_RESOURCES, sn.getResources()) ;
		String whereClause = String.format(TABLE_STUDENT_NUMBER_WHERE_NUMBER, number) ;
		
		int resUpdate =  this.bdd.update(TABLE_STUDENT_NUMBER.getName(), values, 
				whereClause, null) ;
		
		if (opened) {
			this.close() ;
		}// if
		return resUpdate ;
	}// updateStudentNumber(String number, StudentNumber)

	/**
	 * Supprime un numéro étudiant et ses ressources associées de la base de données.
	 *
	 * @param	number
	 * 			numéro étudiant à supprimer
	 * 
	 * @return 	Le nombre de tuples supprimés
	 */
	public int removeStudentNumber(String number) {
		boolean opened = this.open() ;

		String whereClause = String.format(TABLE_STUDENT_NUMBER_WHERE_NUMBER, number) ;
		int resDel = this.bdd.delete(TABLE_STUDENT_NUMBER.getName(), 
				whereClause, null) ;
		
		if (opened) {
			this.close() ;
		}// if
		return resDel ;
	}// removeStudentNumber(String)

	/**
	 * Retourne les ressources associés au numéro étudiant fourni.
	 *
	 * @param	number
	 * 			numéro étudiant dont on recherche les ressources
	 *
	 * @return	instance de {@code StudentNumber} contenant le numéro étudiant fourni
	 * 			et les ressources associés ou {@code null} si aucune ressource n'est
	 * 			connue pour le numéro étudiant fourni
	 */
	public StudentNumber getRessourceWithStudentNumber(String number) {
		boolean opened = this.open() ;
		
		String whereClause = String.format(TABLE_STUDENT_NUMBER_WHERE_NUMBER, number) ;
		Cursor cursor = this.bdd.query(TABLE_STUDENT_NUMBER.getName(),
				TABLE_STUDENT_NUMBER.getColumnNames(),
				whereClause, null, null, null, null, null) ;
		StudentNumber sn = cursorToStudentNumber(cursor) ;
		cursor.close() ;
		
		if (opened) {
			this.close() ;
		}// if
		
		return sn ;
	}// getRessourceWithStudentNumber(String)

	/**
	 * Retourne une instance de {@code StudentNumber} initialisée avec les valeurs
	 * du prochain tuple du curseur fourni.
	 *
	 * @param	cursor
	 * 			source des valeurs pour initialiser les ressources
	 * 
	 * @return	instance de {@code StudentNumber} ou {@code null} si aucune
	 * 			ressource n'a pu être trouvée
	 */
	protected static StudentNumber cursorToStudentNumber(Cursor cursor) {
		StudentNumber sn = null ;
		if ((cursor != null) && cursor.moveToNext()) {
			sn = new StudentNumber() ;
			sn.setStudentNumber(
					getString(cursor, TABLE_STUDENT_NUMBER_COL_NAME_STUDENT_NUMBER)) ;
			sn.setResources(
					getString(cursor, TABLE_STUDENT_NUMBER_COL_NAME_RESOURCES)) ;
		}// if
		return sn ;
	}// cursorToStudentNumber(Cursor)

	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
	 *                                                                                   *
	 * 		Fonctions relatives à l'historique des cours                                 *
	 *                                                                                   * 
	\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
	
	/**
	 * Insère ou met à jour l'historique du cours fourni en utilisant les valeurs
	 * fournies.
	 *
	 * @param	lesson
	 * 			cours dont l'historique doit être inséré/mis à jour
	 * 
	 * @return 	{@code -1} si échec, sinon la ligne du tuple inséré
	 */
	public long replaceLessonHistory(Lesson lesson) {
		boolean opened = this.open() ;
		
		long res = -1 ;
		if (this.getLessonHistory(lesson.getLessonId()) == null) {
			res = this.insertLessonHistory(new LessonHistory(
					lesson.getLessonId(), lesson.getTime(), lesson.getRoom())) ;
		} else {
			// Update previous history
			res = this.updateLessonHistory(lesson.getLessonId(), 
					new LessonHistory(lesson)) ; 
		}// if
		
		if (opened) {
			this.close() ;
		}// if
		return res ;
	}// replaceLessonHistory(Lesson)
	
	/**
	 * Insère l'historique fourni d'un cours.
	 *
	 * @param	history
	 * 			l'historique du cours inséré
	 * 
	 * @return 	{@code -1} si échec, sinon la ligne du tuple inséré
	 */
	public long insertLessonHistory(LessonHistory history) {
		boolean opened = this.open() ;
		
		// No previous history found -> insert
		ContentValues values = new ContentValues() ;
		values.put(TABLE_LESSON_HISTORY_COL_NAME_LESSON_ID, 
				Long.valueOf(history.getId())) ;
		values.put(TABLE_LESSON_HISTORY_COL_NAME_TIME, history.getTime()) ;
		values.put(TABLE_LESSON_HISTORY_COL_NAME_ROOMS, history.getRooms()) ;

		long res = this.lessonHistoryInsert.insert(values) ;
		
		if (opened) {
			this.close() ;
		}// if
		return res ;
	}// insertLessonHistory(LessonHistory)

	/**
	 * Retourne l'historique du cours avec l'identifiant fourni. L'historique
	 * correspond à la valeur du cours avant la dernière mise à jour.
	 *
	 * @param	lessonId
	 * 			identifiant du cours demandé
	 * 
	 * @return	instance de {@code Lesson} représentant l'historique du cours ou 
	 * 			{@code null} si aucun historique n'existe pour l'identifiant fourni
	 */
	public LessonHistory getLessonHistory(long lessonId) {
		boolean opened = this.open() ;

		String whereClause = String.format(TABLE_LESSON_HISTORY_WHERE_ID, 
				Long.valueOf(lessonId)) ;
		Cursor c = this.bdd.query(TABLE_LESSON_HISTORY.getName(),
				TABLE_LESSON_HISTORY.getColumnNames(),
				whereClause, null, null, null, null, null) ;
		LessonHistory history = cursorToLessonHistory(c) ;
		c.close() ;
		
		if (opened) {
			this.close() ;
		}// if
		return history ;
	}// getLessonHistory(long)

	/**
	 * Supprime l'historique d'un cours de la base de données.
	 *
	 * @param	lessonId
	 * 			identifiant du cours dont l'historique est à supprimer
	 * 
	 * @return 	Le nombre de tuples supprimés
	 */
	public int removeLessonHistory(long lessonId) {
		boolean opened = this.open() ;

		String whereClause = String.format(TABLE_LESSON_HISTORY_WHERE_ID, 
				Long.valueOf(lessonId)) ;
		int resDel = 
				this.bdd.delete(TABLE_LESSON_HISTORY.getName(), whereClause, null) ;
		
		if (opened) {
			this.close() ;
		}// if
		return resDel ;
	}// removeLessonHistory(long)

	/**
	 * Met à jour l'historique du cours indiqué.
	 *
	 * @param	lessonId
	 * 			identifiant du cours dont l'historique est à modifier
	 * 
	 * @param 	newLesson
	 * 			nouvelles valeurs de l'historique
	 * 
	 * @return  Le nombre de tuples mis à jour
	 */
	public int updateLessonHistory(long lessonId, LessonHistory newLessonHistory) {
		boolean opened = this.open() ;

		// Retrieve old values
		LessonHistory oldLessonHistory = this.getLessonHistory(lessonId) ;
		
		// Retrieve the changed values
		ContentValues values = new ContentValues() ;
		String newTime = newLessonHistory.getTime() ;
		if (!oldLessonHistory.getTime().equals(newTime)) {
			values.put(TABLE_LESSON_HISTORY_COL_NAME_TIME, newTime) ;
		}// if
		String newRooms = newLessonHistory.getRooms() ;
		if (!oldLessonHistory.getRooms().equals(newRooms)) {
			values.put(TABLE_LESSON_HISTORY_COL_NAME_ROOMS, newRooms) ;
		}// if
		
		// Update data base
		String whereClause = String.format(TABLE_LESSON_HISTORY_WHERE_ID, 
				Long.valueOf(lessonId)) ; 
		int resUpt = this.bdd.update(TABLE_LESSON_HISTORY.getName(), 
				values, whereClause, null) ;
		
		if (opened) {
			this.close() ;
		}// if
		return resUpt ;
	}// updateLessonHistory(long, Lesson)
	
//	public void fetchMySchedule() {
//		Log.d(TAG, "Attempting to retrieve my old schedule") ; //$NON-NLS-1$
//		this.open() ;
//		Cursor cursor = this.bdd.query("LESSON", //$NON-NLS-1$
//				new String[]{"_id", "TITLE", "TIME", "SCHEDULE", "TYPE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
//				"CHANGE", "ROOM", "GROUPS", "TEACHER", "COLOR", "NOTE", "LASTMODIF"},  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
//				null, null, null, null, null, null) ;
//		while (cursor.moveToNext()) {
//			StringBuilder line = new StringBuilder("new Lesson(") ; //$NON-NLS-1$
//			line.append('"') ; line.append(cursor.getString(1)) ;line.append('"') ;
//			line.append(",") ; //$NON-NLS-1$
//			line.append('"') ; line.append(cursor.getString(2)) ;line.append('"') ;
//			line.append(",") ; //$NON-NLS-1$
//			line.append("scheduleId") ; //$NON-NLS-1$
//			line.append(",") ; //$NON-NLS-1$
//			line.append("LessonType.NORMAL") ; line.append(",") ; //$NON-NLS-1$ //$NON-NLS-2$
//			line.append(cursor.getString(5)) ;line.append(",") ; //$NON-NLS-1$
//			line.append('"') ; line.append(cursor.getString(6)) ;line.append('"') ;
//			line.append(",") ; //$NON-NLS-1$
//			line.append('"') ; line.append(cursor.getString(7)) ;line.append('"') ;
//			line.append(",") ; //$NON-NLS-1$
//			line.append('"') ; line.append(cursor.getString(8)) ;line.append('"') ;
//			line.append(",") ; //$NON-NLS-1$
//			line.append('"') ; line.append(cursor.getString(9)) ;line.append('"') ;
//			line.append(",") ; //$NON-NLS-1$
//			line.append('"') ; line.append(cursor.getString(10)) ;line.append('"') ;
//			line.append(",") ; //$NON-NLS-1$
//			line.append('"') ; line.append(cursor.getString(11)) ;line.append('"') ;
//			line.append(")") ; //$NON-NLS-1$
//			Log.d(TAG, line.toString()) ;
//		}// while
//		cursor.close() ;
//		this.close() ;
//		
//		Log.d(TAG, "Finished retrieving my old schedule") ; //$NON-NLS-1$
//	}// fetchMySchedule()
	
}// class Database
